﻿using ScintillaNET;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Text;
using System.Windows.Forms;
using WeifenLuo.WinFormsUI.Docking;

namespace Editor
{
    public partial class FormMain : Form
    {
        #region Constants

        private const string NEW_DOCUMENT_TEXT = "Untitled";

        #endregion Constants

        #region Fields

        private int _newDocumentCount = 0;
        private int _zoomLevel;
        private DeserializeDockContent _deserializeDockContent;
        private FormProjectExplorer _projectExplorer;
        private Dictionary<string, string> _extensionMap;

        #endregion Fields

        #region Properties

        public FormDocument ActiveDocument
        {
            get
            {
                return dockPanel.ActiveDocument as FormDocument;
            }
        }

        #endregion Properties

        #region Methods

        public FormMain()
        {
            InitializeComponent();

            LoadExtensionConfig();

            _projectExplorer = new FormProjectExplorer();

            _deserializeDockContent = new DeserializeDockContent(GetContentFromPersistString);
        }

        private void LoadExtensionConfig()
        { 
            string extensionConfigFile = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "Extension.config");

            _extensionMap = new Dictionary<string, string>();

            if (File.Exists(extensionConfigFile))
            {
                string[] lines = File.ReadAllLines(extensionConfigFile);

                for (int i = 0; i < lines.Length; i++)
                {
                    string[] kvpair = lines[i].Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);

                    if (kvpair.Length == 2)
                    {
                        string[] exts = kvpair[1].Split(new char[] { '|' });

                        for (int j = 0; j < exts.Length; j++)
                        {
                            _extensionMap["." + exts[j]] = kvpair[0];
                        }
                    }
                }
            }
        }


        private IDockContent GetContentFromPersistString(string persistString)
        {
            if (persistString == typeof(FormProjectExplorer).ToString())
                return _projectExplorer;
            else
            {
                string[] parsedStrings = persistString.Split(new char[] { '*' });
                if (parsedStrings.Length != 2)
                    return null;

                if (parsedStrings[0] != typeof(FormDocument).ToString())
                    return null;

                if (parsedStrings[1] != string.Empty)
                {
                    return OpenFile(parsedStrings[1]);
                }

                return null;
            }
        }

        private void FormMain_Load(object sender, EventArgs e)
        {
            string layoutConfigFile = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "Layout.config");

            if (File.Exists(layoutConfigFile))
                dockPanel.LoadFromXml(layoutConfigFile, _deserializeDockContent);
        }

        private void FormMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            string layoutConfigFile = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "Layout.config");
            dockPanel.SaveAsXml(layoutConfigFile);
        }

        private string GetLanguageFromExtension(string extension)
        {
            string lang = "";

            if (_extensionMap.TryGetValue(extension, out lang))
                return lang;
            
            return string.Empty;
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
        }


        private void autoCompleteToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ActiveDocument.Scintilla.AutoComplete.Show();
        }


        private void clearBookmarksToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ActiveDocument.Scintilla.Markers.DeleteAll(0);
        }


        private void closeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (ActiveDocument != null)
                ActiveDocument.Close();
        }


        private void collectToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ActiveDocument.Scintilla.DropMarkers.Collect();
        }


        private void commentLineToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ActiveDocument.Scintilla.Commands.Execute(BindableCommand.LineComment);
        }


        private void commentStreamToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ActiveDocument.Scintilla.Commands.Execute(BindableCommand.StreamComment);
        }


        private void copyToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (ActiveDocument != null)
                ActiveDocument.Scintilla.Clipboard.Copy();
        }


        private void cutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (ActiveDocument != null)
                ActiveDocument.Scintilla.Clipboard.Cut();
        }


        private void dockPanel_ActiveDocumentChanged(object sender, EventArgs e)
        {
            // Update the main form _text to show the current document
            if (ActiveDocument != null)
                this.Text = String.Format(CultureInfo.CurrentCulture, "[{0}] - {1}", ActiveDocument.Text, Program.Title);
            else
                this.Text = Program.Title;
        }


        private void dropToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ActiveDocument.Scintilla.DropMarkers.Drop();
        }


        private void endOfLineToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Toggle EOL visibility for all open files
            endOfLineToolStripMenuItem.Checked = !endOfLineToolStripMenuItem.Checked;
            foreach (FormDocument doc in dockPanel.Documents)
            {
                doc.Scintilla.EndOfLine.IsVisible = endOfLineToolStripMenuItem.Checked;
            }
        }


        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Close();
        }


        private void findInFilesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Coming someday...
        }


        private void findToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ActiveDocument.Scintilla.FindReplace.ShowFind();
        }


        private void foldAllToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (ActiveDocument != null)
            {
                foreach (Line l in ActiveDocument.Scintilla.Lines)
                {
                    l.FoldExpanded = true;
                }
            }
        }


        private void foldLevelToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (ActiveDocument != null)
                ActiveDocument.Scintilla.Lines.Current.FoldExpanded = true;
        }


        private void goToToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ActiveDocument.Scintilla.GoTo.ShowGoToDialog();
        }


        private void insertSnippetToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ActiveDocument.Scintilla.Snippets.ShowSnippetList();
        }


        private void lineNumbersToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Toggle the line numbers margin for all documents
            lineNumbersToolStripMenuItem.Checked = !lineNumbersToolStripMenuItem.Checked;
            foreach (FormDocument docForm in dockPanel.Documents)
            {
                if (lineNumbersToolStripMenuItem.Checked)
                    docForm.Scintilla.Margins.Margin0.Width = docForm.CalcLineWidth();
                else
                    docForm.Scintilla.Margins.Margin0.Width = 0;
            }
        }


        private void makeLowerCaseToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ActiveDocument.Scintilla.Commands.Execute(BindableCommand.LowerCase);
        }


        private void makeUpperCaseToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ActiveDocument.Scintilla.Commands.Execute(BindableCommand.UpperCase);
        }


        private void navigateBackwardToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (ActiveDocument != null)
                ActiveDocument.Scintilla.DocumentNavigation.NavigateBackward();
        }


        private void navigateForwardToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (ActiveDocument != null)
                ActiveDocument.Scintilla.DocumentNavigation.NavigateForward();
        }


        private FormDocument NewDocument()
        {
            FormDocument doc = new FormDocument();
            SetScintillaToCurrentOptions(doc);
            doc.Text = String.Format(CultureInfo.CurrentCulture, "{0}{1}", NEW_DOCUMENT_TEXT, ++_newDocumentCount);
            doc.Show(dockPanel);
            return doc;
        }


        private void newToolStripMenuItem_Click(object sender, EventArgs e)
        {
            NewDocument();
        }


        private void nextBookmarkToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // I've got to redo this whole FindNextMarker/FindPreviousMarker Scheme
            Line l = ActiveDocument.Scintilla.Lines.Current.FindNextMarker(1);
            if (l != null)
                l.Goto();
        }


        private void OpenFile()
        {
            if (openFileDialog.ShowDialog() != DialogResult.OK)
                return;

            foreach (string filePath in openFileDialog.FileNames)
            {
                // Ensure this file isn't already open
                bool isOpen = false;
                foreach (FormDocument documentForm in dockPanel.Documents)
                {
                    if (filePath.Equals(documentForm.FilePath, StringComparison.OrdinalIgnoreCase))
                    {
                        documentForm.Select();
                        isOpen = true;
                        break;
                    }
                }

                // Open the files
                if (!isOpen)
                    OpenFile(filePath);
            }
        }


        private FormDocument OpenFile(string filePath)
        {
            string extension = Path.GetExtension(filePath).ToLower();
            string language = GetLanguageFromExtension(extension);

            FormDocument doc = new FormDocument();
            SetScintillaToCurrentOptions(doc);
            doc.Scintilla.Text = File.ReadAllText(filePath);
            doc.Scintilla.UndoRedo.EmptyUndoBuffer();
            doc.Scintilla.Modified = false;
            doc.Text = Path.GetFileName(filePath);
            doc.FilePath = filePath;
            doc.Show(dockPanel);

            doc.Scintilla.ConfigurationManager.Language = language;

            return doc;
        }


        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            OpenFile();
        }


        private void pasteToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (ActiveDocument != null)
                ActiveDocument.Scintilla.Clipboard.Paste();
        }


        private void previosBookmarkToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // I've got to redo this whole FindNextMarker/FindPreviousMarker Scheme
            Line l = ActiveDocument.Scintilla.Lines.Current.FindPreviousMarker(1);
            if (l != null)
                l.Goto();
        }


        private void printPreviewToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (ActiveDocument != null)
                ActiveDocument.Scintilla.Printing.PrintPreview();
        }


        private void printToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (ActiveDocument != null)
                ActiveDocument.Scintilla.Printing.Print();
        }


        private void redoToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (ActiveDocument != null)
                ActiveDocument.Scintilla.UndoRedo.Redo();
        }


        private void replaceInFilesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //	Coming someday...
        }


        private void replaceToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ActiveDocument.Scintilla.FindReplace.ShowReplace();
        }


        private void resetZoomToolStripMenuItem_Click(object sender, EventArgs e)
        {
            _zoomLevel = 0;
            UpdateAllScintillaZoom();
        }


        private void saveAllStripMenuItem_Click(object sender, EventArgs e)
        {
            foreach (FormDocument doc in dockPanel.Documents)
            {
                doc.Activate();
                doc.Save();
            }
        }


        private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (ActiveDocument != null)
                ActiveDocument.SaveAs();
        }


        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (ActiveDocument != null)
                ActiveDocument.Save();
        }


        private void selectAllToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (ActiveDocument != null)
                ActiveDocument.Scintilla.Selection.SelectAll();
        }

        private void SetScintillaToCurrentOptions(FormDocument doc)
        {
            // Turn on line numbers?
            if (!lineNumbersToolStripMenuItem.Checked)
                doc.Scintilla.Margins.Margin0.Width = 0;

            // Turn on white space?
            if (whitespaceToolStripMenuItem.Checked)
                doc.Scintilla.Whitespace.Mode = WhitespaceMode.VisibleAlways;
            else
                doc.Scintilla.Whitespace.Mode = WhitespaceMode.Invisible;

            // Turn on word wrap?
            if (wordWrapToolStripMenuItem.Checked)
                doc.Scintilla.LineWrapping.Mode = LineWrappingMode.Word;
            else
                doc.Scintilla.LineWrapping.Mode = LineWrappingMode.None;

            // Show EOL?
            doc.Scintilla.EndOfLine.IsVisible = endOfLineToolStripMenuItem.Checked;

            // Set the zoom
            doc.Scintilla.ZoomFactor = _zoomLevel;
        }


        private void statusBarToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Toggle the visibility of the status strip
            statusStrip.Visible = !statusStrip.Visible;
            statusBarToolStripMenuItem.Checked = statusStrip.Visible;
        }


        private void surroundWithToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ActiveDocument.Scintilla.Snippets.ShowSurroundWithList();
        }


        private void toggleBookmarkToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Line currentLine = ActiveDocument.Scintilla.Lines.Current;
            if (ActiveDocument.Scintilla.Markers.GetMarkerMask(currentLine) == 0)
            {
                currentLine.AddMarker(0);
            }
            else
            {
                currentLine.DeleteMarker(0);
            }
        }


        private void toolBarToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Toggle the visibility of the tool bar
            toolStrip.Visible = !toolStrip.Visible;
            toolBarToolStripMenuItem.Checked = toolStrip.Visible;
        }


        private void uncommentLineToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ActiveDocument.Scintilla.Commands.Execute(BindableCommand.LineUncomment);
        }


        private void undoToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (ActiveDocument != null)
                ActiveDocument.Scintilla.UndoRedo.Undo();
        }


        private void unfoldAllToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (ActiveDocument != null)
            {
                foreach (Line l in ActiveDocument.Scintilla.Lines)
                {
                    l.FoldExpanded = true;
                }
            }
        }


        private void unfoldLevelToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (ActiveDocument != null)
                ActiveDocument.Scintilla.Lines.Current.FoldExpanded = false;
        }


        private void UpdateAllScintillaZoom()
        {
            // Update zoom level for all files
            foreach (FormDocument doc in dockPanel.Documents)
                doc.Scintilla.ZoomFactor = _zoomLevel;
        }


        private void whitespaceToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Toggle the whitespace mode for all open files
            whitespaceToolStripMenuItem.Checked = !whitespaceToolStripMenuItem.Checked;
            foreach (FormDocument doc in dockPanel.Documents)
            {
                if (whitespaceToolStripMenuItem.Checked)
                    doc.Scintilla.Whitespace.Mode = WhitespaceMode.VisibleAlways;
                else
                    doc.Scintilla.Whitespace.Mode = WhitespaceMode.Invisible;
            }
        }


        private void wordWrapToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Toggle word wrap for all open files
            wordWrapToolStripMenuItem.Checked = !wordWrapToolStripMenuItem.Checked;
            foreach (FormDocument doc in dockPanel.Documents)
            {
                if (wordWrapToolStripMenuItem.Checked)
                    doc.Scintilla.LineWrapping.Mode = LineWrappingMode.Word;
                else
                    doc.Scintilla.LineWrapping.Mode = LineWrappingMode.None;
            }
        }


        private void zoomInToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Increase the zoom for all open files
            _zoomLevel++;
            UpdateAllScintillaZoom();
        }


        private void zoomOutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            _zoomLevel--;
            UpdateAllScintillaZoom();
        }

        private void closeWindowToolStripMenuItem_Click(object sender, EventArgs e)
        {

        }

        private void projectExplorerToolStripMenuItem_Click(object sender, EventArgs e)
        {
            _projectExplorer.Show(dockPanel);
        }

        #endregion Methods
    }
}
